// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library vm.bytecode.assembler;

import 'dart:typed_data';

import 'package:vm/bytecode/dbc.dart';

class Label {
  List<int> _jumps = <int>[];
  int offset = -1;

  Label();

  bool get isBound => offset >= 0;

  int jumpOperand(int jumpOffset) {
    if (isBound) {
      // Jump instruction takes an offset in DBC words.
      return (offset - jumpOffset) >> BytecodeAssembler.kLog2BytesPerBytecode;
    }
    _jumps.add(jumpOffset);
    return 0;
  }

  List<int> bind(int offset) {
    assert(!isBound);
    this.offset = offset;
    final jumps = _jumps;
    _jumps = null;
    return jumps;
  }
}

class BytecodeAssembler {
  static const int kBitsPerInt = 64;
  static const int kLog2BytesPerBytecode = 2;

  // TODO(alexmarkov): figure out more efficient storage for generated bytecode.
  final List<int> bytecode = new List<int>();
  final Uint32List _encodeBufferIn;
  final Uint8List _encodeBufferOut;

  BytecodeAssembler._(this._encodeBufferIn, this._encodeBufferOut);

  factory BytecodeAssembler() {
    final buf = new Uint32List(1);
    return new BytecodeAssembler._(buf, new Uint8List.view(buf.buffer));
  }

  int get offset => bytecode.length;

  void bind(Label label) {
    final List<int> jumps = label.bind(offset);
    for (int jumpOffset in jumps) {
      patchJump(jumpOffset, label.jumpOperand(jumpOffset));
    }
  }

  void emitWord(int word) {
    _encodeBufferIn[0] = word; // TODO(alexmarkov): Which endianness to use?
    bytecode.addAll(_encodeBufferOut);
  }

  void _setWord(int pos, int word) {
    _encodeBufferIn[0] = word; // TODO(alexmarkov): Which endianness to use?
    bytecode.setRange(pos, pos + _encodeBufferOut.length, _encodeBufferOut);
  }

  int _unsigned(int v, int bits) {
    assert(bits < kBitsPerInt);
    final int mask = (1 << bits) - 1;
    if ((v & mask) != v) {
      throw 'Value $v is out of unsigned $bits-bit range';
    }
    return v;
  }

  int _signed(int v, int bits) {
    assert(bits < kBitsPerInt);
    final int shift = kBitsPerInt - bits;
    if (((v << shift) >> shift) != v) {
      throw 'Value $v is out of signed $bits-bit range';
    }
    final int mask = (1 << bits) - 1;
    return v & mask;
  }

  int _uint8(int v) => _unsigned(v, 8);
  int _uint16(int v) => _unsigned(v, 16);

  int _int8(int v) => _signed(v, 8);
  int _int16(int v) => _signed(v, 16);
  int _int24(int v) => _signed(v, 24);

  int _encode0(Opcode opcode) => _uint8(opcode.index);

  int _encodeA(Opcode opcode, int ra) =>
      _uint8(opcode.index) | (_uint8(ra) << 8);

  int _encodeAD(Opcode opcode, int ra, int rd) =>
      _uint8(opcode.index) | (_uint8(ra) << 8) | (_uint16(rd) << 16);

  int _encodeAX(Opcode opcode, int ra, int rx) =>
      _uint8(opcode.index) | (_uint8(ra) << 8) | (_int16(rx) << 16);

  int _encodeD(Opcode opcode, int rd) =>
      _uint8(opcode.index) | (_uint16(rd) << 16);

  int _encodeX(Opcode opcode, int rx) =>
      _uint8(opcode.index) | (_int16(rx) << 16);

  int _encodeABC(Opcode opcode, int ra, int rb, int rc) =>
      _uint8(opcode.index) |
      (_uint8(ra) << 8) |
      (_uint8(rb) << 16) |
      (_uint8(rc) << 24);

  int _encodeABY(Opcode opcode, int ra, int rb, int ry) =>
      _uint8(opcode.index) |
      (_uint8(ra) << 8) |
      (_uint8(rb) << 16) |
      (_int8(ry) << 24);

  int _encodeT(Opcode opcode, int rt) =>
      _uint8(opcode.index) | (_int24(rt) << 8);

  void emitTrap() {
    emitWord(_encode0(Opcode.kTrap));
  }

  void emitNop(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kNop, ra, rd));
  }

  void emitCompile() {
    emitWord(_encode0(Opcode.kCompile));
  }

  void emitHotCheck(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kHotCheck, ra, rd));
  }

  void emitIntrinsic(int ra) {
    emitWord(_encodeA(Opcode.kIntrinsic, ra));
  }

  void emitDrop1() {
    emitWord(_encode0(Opcode.kDrop1));
  }

  void emitDropR(int ra) {
    emitWord(_encodeA(Opcode.kDropR, ra));
  }

  void emitDrop(int ra) {
    emitWord(_encodeA(Opcode.kDrop, ra));
  }

  void emitJump(Label label) {
    emitWord(_encodeT(Opcode.kJump, label.jumpOperand(offset)));
  }

  void patchJump(int pos, int rt) {
    _setWord(pos, _encodeT(Opcode.kJump, rt));
  }

  void emitReturn(int ra) {
    emitWord(_encodeA(Opcode.kReturn, ra));
  }

  void emitReturnTOS() {
    emitWord(_encode0(Opcode.kReturnTOS));
  }

  void emitMove(int ra, int rx) {
    emitWord(_encodeAX(Opcode.kMove, ra, rx));
  }

  void emitSwap(int ra, int rx) {
    emitWord(_encodeAX(Opcode.kSwap, ra, rx));
  }

  void emitPush(int rx) {
    emitWord(_encodeX(Opcode.kPush, rx));
  }

  void emitLoadConstant(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kLoadConstant, ra, rd));
  }

  void emitLoadClassId(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kLoadClassId, ra, rd));
  }

  void emitLoadClassIdTOS() {
    emitWord(_encode0(Opcode.kLoadClassIdTOS));
  }

  void emitPushConstant(int rd) {
    emitWord(_encodeD(Opcode.kPushConstant, rd));
  }

  void emitStoreLocal(int rx) {
    emitWord(_encodeX(Opcode.kStoreLocal, rx));
  }

  void emitPopLocal(int rx) {
    emitWord(_encodeX(Opcode.kPopLocal, rx));
  }

  void emitIndirectStaticCall(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIndirectStaticCall, ra, rd));
  }

  void emitStaticCall(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kStaticCall, ra, rd));
  }

  void emitInstanceCall1(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kInstanceCall1, ra, rd));
  }

  void emitInstanceCall2(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kInstanceCall2, ra, rd));
  }

  void emitInstanceCall1Opt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kInstanceCall1Opt, ra, rd));
  }

  void emitInstanceCall2Opt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kInstanceCall2Opt, ra, rd));
  }

  void emitPushPolymorphicInstanceCall(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kPushPolymorphicInstanceCall, ra, rd));
  }

  void emitPushPolymorphicInstanceCallByRange(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kPushPolymorphicInstanceCallByRange, ra, rd));
  }

  void emitNativeCall(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kNativeCall, ra, rb, rc));
  }

  void emitOneByteStringFromCharCode(int ra, int rx) {
    emitWord(_encodeAX(Opcode.kOneByteStringFromCharCode, ra, rx));
  }

  void emitStringToCharCode(int ra, int rx) {
    emitWord(_encodeAX(Opcode.kStringToCharCode, ra, rx));
  }

  void emitAddTOS() {
    emitWord(_encode0(Opcode.kAddTOS));
  }

  void emitSubTOS() {
    emitWord(_encode0(Opcode.kSubTOS));
  }

  void emitMulTOS() {
    emitWord(_encode0(Opcode.kMulTOS));
  }

  void emitBitOrTOS() {
    emitWord(_encode0(Opcode.kBitOrTOS));
  }

  void emitBitAndTOS() {
    emitWord(_encode0(Opcode.kBitAndTOS));
  }

  void emitEqualTOS() {
    emitWord(_encode0(Opcode.kEqualTOS));
  }

  void emitLessThanTOS() {
    emitWord(_encode0(Opcode.kLessThanTOS));
  }

  void emitGreaterThanTOS() {
    emitWord(_encode0(Opcode.kGreaterThanTOS));
  }

  void emitSmiAddTOS() {
    emitWord(_encode0(Opcode.kSmiAddTOS));
  }

  void emitSmiSubTOS() {
    emitWord(_encode0(Opcode.kSmiSubTOS));
  }

  void emitSmiMulTOS() {
    emitWord(_encode0(Opcode.kSmiMulTOS));
  }

  void emitSmiBitAndTOS() {
    emitWord(_encode0(Opcode.kSmiBitAndTOS));
  }

  void emitAdd(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kAdd, ra, rb, rc));
  }

  void emitSub(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kSub, ra, rb, rc));
  }

  void emitMul(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kMul, ra, rb, rc));
  }

  void emitDiv(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDiv, ra, rb, rc));
  }

  void emitMod(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kMod, ra, rb, rc));
  }

  void emitShl(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kShl, ra, rb, rc));
  }

  void emitShr(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kShr, ra, rb, rc));
  }

  void emitShlImm(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kShlImm, ra, rb, rc));
  }

  void emitNeg(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kNeg, ra, rd));
  }

  void emitBitOr(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kBitOr, ra, rb, rc));
  }

  void emitBitAnd(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kBitAnd, ra, rb, rc));
  }

  void emitBitXor(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kBitXor, ra, rb, rc));
  }

  void emitBitNot(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kBitNot, ra, rd));
  }

  void emitMin(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kMin, ra, rb, rc));
  }

  void emitMax(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kMax, ra, rb, rc));
  }

  void emitWriteIntoDouble(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kWriteIntoDouble, ra, rd));
  }

  void emitUnboxDouble(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kUnboxDouble, ra, rd));
  }

  void emitCheckedUnboxDouble(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckedUnboxDouble, ra, rd));
  }

  void emitUnboxInt32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kUnboxInt32, ra, rb, rc));
  }

  void emitBoxInt32(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kBoxInt32, ra, rd));
  }

  void emitBoxUint32(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kBoxUint32, ra, rd));
  }

  void emitSmiToDouble(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kSmiToDouble, ra, rd));
  }

  void emitDoubleToSmi(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDoubleToSmi, ra, rd));
  }

  void emitDAdd(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDAdd, ra, rb, rc));
  }

  void emitDSub(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDSub, ra, rb, rc));
  }

  void emitDMul(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDMul, ra, rb, rc));
  }

  void emitDDiv(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDDiv, ra, rb, rc));
  }

  void emitDNeg(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDNeg, ra, rd));
  }

  void emitDSqrt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDSqrt, ra, rd));
  }

  void emitDMin(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDMin, ra, rb, rc));
  }

  void emitDMax(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDMax, ra, rb, rc));
  }

  void emitDCos(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDCos, ra, rd));
  }

  void emitDSin(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDSin, ra, rd));
  }

  void emitDPow(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDPow, ra, rb, rc));
  }

  void emitDMod(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kDMod, ra, rb, rc));
  }

  void emitDTruncate(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDTruncate, ra, rd));
  }

  void emitDFloor(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDFloor, ra, rd));
  }

  void emitDCeil(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDCeil, ra, rd));
  }

  void emitDoubleToFloat(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDoubleToFloat, ra, rd));
  }

  void emitFloatToDouble(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kFloatToDouble, ra, rd));
  }

  void emitDoubleIsNaN(int ra) {
    emitWord(_encodeA(Opcode.kDoubleIsNaN, ra));
  }

  void emitDoubleIsInfinite(int ra) {
    emitWord(_encodeA(Opcode.kDoubleIsInfinite, ra));
  }

  void emitStoreStaticTOS(int rd) {
    emitWord(_encodeD(Opcode.kStoreStaticTOS, rd));
  }

  void emitPushStatic(int rd) {
    emitWord(_encodeD(Opcode.kPushStatic, rd));
  }

  void emitInitStaticTOS() {
    emitWord(_encode0(Opcode.kInitStaticTOS));
  }

  void emitIfNeStrictTOS() {
    emitWord(_encode0(Opcode.kIfNeStrictTOS));
  }

  void emitIfEqStrictTOS() {
    emitWord(_encode0(Opcode.kIfEqStrictTOS));
  }

  void emitIfNeStrictNumTOS() {
    emitWord(_encode0(Opcode.kIfNeStrictNumTOS));
  }

  void emitIfEqStrictNumTOS() {
    emitWord(_encode0(Opcode.kIfEqStrictNumTOS));
  }

  void emitIfSmiLtTOS() {
    emitWord(_encode0(Opcode.kIfSmiLtTOS));
  }

  void emitIfSmiLeTOS() {
    emitWord(_encode0(Opcode.kIfSmiLeTOS));
  }

  void emitIfSmiGeTOS() {
    emitWord(_encode0(Opcode.kIfSmiGeTOS));
  }

  void emitIfSmiGtTOS() {
    emitWord(_encode0(Opcode.kIfSmiGtTOS));
  }

  void emitIfNeStrict(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfNeStrict, ra, rd));
  }

  void emitIfEqStrict(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfEqStrict, ra, rd));
  }

  void emitIfLe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfLe, ra, rd));
  }

  void emitIfLt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfLt, ra, rd));
  }

  void emitIfGe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfGe, ra, rd));
  }

  void emitIfGt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfGt, ra, rd));
  }

  void emitIfULe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfULe, ra, rd));
  }

  void emitIfULt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfULt, ra, rd));
  }

  void emitIfUGe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfUGe, ra, rd));
  }

  void emitIfUGt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfUGt, ra, rd));
  }

  void emitIfDNe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDNe, ra, rd));
  }

  void emitIfDEq(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDEq, ra, rd));
  }

  void emitIfDLe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDLe, ra, rd));
  }

  void emitIfDLt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDLt, ra, rd));
  }

  void emitIfDGe(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDGe, ra, rd));
  }

  void emitIfDGt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfDGt, ra, rd));
  }

  void emitIfNeStrictNum(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfNeStrictNum, ra, rd));
  }

  void emitIfEqStrictNum(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kIfEqStrictNum, ra, rd));
  }

  void emitIfEqNull(int ra) {
    emitWord(_encodeA(Opcode.kIfEqNull, ra));
  }

  void emitIfNeNull(int ra) {
    emitWord(_encodeA(Opcode.kIfNeNull, ra));
  }

  void emitCreateArrayTOS() {
    emitWord(_encode0(Opcode.kCreateArrayTOS));
  }

  void emitCreateArrayOpt(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kCreateArrayOpt, ra, rb, rc));
  }

  void emitAllocate(int rd) {
    emitWord(_encodeD(Opcode.kAllocate, rd));
  }

  void emitAllocateT() {
    emitWord(_encode0(Opcode.kAllocateT));
  }

  void emitAllocateOpt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kAllocateOpt, ra, rd));
  }

  void emitAllocateTOpt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kAllocateTOpt, ra, rd));
  }

  void emitStoreIndexedTOS() {
    emitWord(_encode0(Opcode.kStoreIndexedTOS));
  }

  void emitStoreIndexed(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexed, ra, rb, rc));
  }

  void emitStoreIndexedUint8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedUint8, ra, rb, rc));
  }

  void emitStoreIndexedExternalUint8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedExternalUint8, ra, rb, rc));
  }

  void emitStoreIndexedOneByteString(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedOneByteString, ra, rb, rc));
  }

  void emitStoreIndexedUint32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedUint32, ra, rb, rc));
  }

  void emitStoreIndexedFloat32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedFloat32, ra, rb, rc));
  }

  void emitStoreIndexed4Float32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexed4Float32, ra, rb, rc));
  }

  void emitStoreIndexedFloat64(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexedFloat64, ra, rb, rc));
  }

  void emitStoreIndexed8Float64(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreIndexed8Float64, ra, rb, rc));
  }

  void emitNoSuchMethod() {
    emitWord(_encode0(Opcode.kNoSuchMethod));
  }

  void emitTailCall() {
    emitWord(_encode0(Opcode.kTailCall));
  }

  void emitTailCallOpt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kTailCallOpt, ra, rd));
  }

  void emitLoadArgDescriptor() {
    emitWord(_encode0(Opcode.kLoadArgDescriptor));
  }

  void emitLoadArgDescriptorOpt(int ra) {
    emitWord(_encodeA(Opcode.kLoadArgDescriptorOpt, ra));
  }

  void emitLoadFpRelativeSlot(int rx) {
    emitWord(_encodeX(Opcode.kLoadFpRelativeSlot, rx));
  }

  void emitLoadFpRelativeSlotOpt(int ra, int rb, int ry) {
    emitWord(_encodeABY(Opcode.kLoadFpRelativeSlotOpt, ra, rb, ry));
  }

  void emitStoreFpRelativeSlot(int rx) {
    emitWord(_encodeX(Opcode.kStoreFpRelativeSlot, rx));
  }

  void emitStoreFpRelativeSlotOpt(int ra, int rb, int ry) {
    emitWord(_encodeABY(Opcode.kStoreFpRelativeSlotOpt, ra, rb, ry));
  }

  void emitLoadIndexedTOS() {
    emitWord(_encode0(Opcode.kLoadIndexedTOS));
  }

  void emitLoadIndexed(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexed, ra, rb, rc));
  }

  void emitLoadIndexedUint8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedUint8, ra, rb, rc));
  }

  void emitLoadIndexedInt8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedInt8, ra, rb, rc));
  }

  void emitLoadIndexedInt32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedInt32, ra, rb, rc));
  }

  void emitLoadIndexedUint32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedUint32, ra, rb, rc));
  }

  void emitLoadIndexedExternalUint8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedExternalUint8, ra, rb, rc));
  }

  void emitLoadIndexedExternalInt8(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedExternalInt8, ra, rb, rc));
  }

  void emitLoadIndexedFloat32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedFloat32, ra, rb, rc));
  }

  void emitLoadIndexed4Float32(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexed4Float32, ra, rb, rc));
  }

  void emitLoadIndexedFloat64(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedFloat64, ra, rb, rc));
  }

  void emitLoadIndexed8Float64(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexed8Float64, ra, rb, rc));
  }

  void emitLoadIndexedOneByteString(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedOneByteString, ra, rb, rc));
  }

  void emitLoadIndexedTwoByteString(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadIndexedTwoByteString, ra, rb, rc));
  }

  void emitStoreField(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kStoreField, ra, rb, rc));
  }

  void emitStoreFieldExt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kStoreFieldExt, ra, rd));
  }

  void emitStoreFieldTOS(int rd) {
    emitWord(_encodeD(Opcode.kStoreFieldTOS, rd));
  }

  void emitLoadField(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadField, ra, rb, rc));
  }

  void emitLoadFieldExt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kLoadFieldExt, ra, rd));
  }

  void emitLoadUntagged(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kLoadUntagged, ra, rb, rc));
  }

  void emitLoadFieldTOS(int rd) {
    emitWord(_encodeD(Opcode.kLoadFieldTOS, rd));
  }

  void emitBooleanNegateTOS() {
    emitWord(_encode0(Opcode.kBooleanNegateTOS));
  }

  void emitBooleanNegate(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kBooleanNegate, ra, rd));
  }

  void emitThrow(int ra) {
    emitWord(_encodeA(Opcode.kThrow, ra));
  }

  void emitEntry(int rd) {
    emitWord(_encodeD(Opcode.kEntry, rd));
  }

  void emitEntryOptimized(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kEntryOptimized, ra, rd));
  }

  void emitFrame(int rd) {
    emitWord(_encodeD(Opcode.kFrame, rd));
  }

  void emitSetFrame(int ra) {
    emitWord(_encodeA(Opcode.kSetFrame, ra));
  }

  void emitAllocateContext(int rd) {
    emitWord(_encodeD(Opcode.kAllocateContext, rd));
  }

  void emitAllocateUninitializedContext(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kAllocateUninitializedContext, ra, rd));
  }

  void emitCloneContext() {
    emitWord(_encode0(Opcode.kCloneContext));
  }

  void emitMoveSpecial(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kMoveSpecial, ra, rd));
  }

  void emitInstantiateType(int rd) {
    emitWord(_encodeD(Opcode.kInstantiateType, rd));
  }

  void emitInstantiateTypeArgumentsTOS(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kInstantiateTypeArgumentsTOS, ra, rd));
  }

  void emitInstanceOf() {
    emitWord(_encode0(Opcode.kInstanceOf));
  }

  void emitBadTypeError() {
    emitWord(_encode0(Opcode.kBadTypeError));
  }

  void emitAssertAssignable(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kAssertAssignable, ra, rd));
  }

  void emitAssertSubtype() {
    emitWord(_encode0(Opcode.kAssertSubtype));
  }

  void emitAssertBoolean(int ra) {
    emitWord(_encodeA(Opcode.kAssertBoolean, ra));
  }

  void emitTestSmi(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kTestSmi, ra, rd));
  }

  void emitTestCids(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kTestCids, ra, rd));
  }

  void emitCheckSmi(int ra) {
    emitWord(_encodeA(Opcode.kCheckSmi, ra));
  }

  void emitCheckEitherNonSmi(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckEitherNonSmi, ra, rd));
  }

  void emitCheckClassId(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckClassId, ra, rd));
  }

  void emitCheckClassIdRange(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckClassIdRange, ra, rd));
  }

  void emitCheckBitTest(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckBitTest, ra, rd));
  }

  void emitCheckCids(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kCheckCids, ra, rb, rc));
  }

  void emitCheckCidsByRange(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kCheckCidsByRange, ra, rb, rc));
  }

  void emitCheckStack() {
    emitWord(_encode0(Opcode.kCheckStack));
  }

  void emitCheckStackAlwaysExit() {
    emitWord(_encode0(Opcode.kCheckStackAlwaysExit));
  }

  void emitCheckFunctionTypeArgs(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kCheckFunctionTypeArgs, ra, rd));
  }

  void emitDebugStep() {
    emitWord(_encode0(Opcode.kDebugStep));
  }

  void emitDebugBreak(int ra) {
    emitWord(_encodeA(Opcode.kDebugBreak, ra));
  }

  void emitDeopt(int ra, int rd) {
    emitWord(_encodeAD(Opcode.kDeopt, ra, rd));
  }

  void emitDeoptRewind() {
    emitWord(_encode0(Opcode.kDeoptRewind));
  }

  void emitEntryOptional(int ra, int rb, int rc) {
    emitWord(_encodeABC(Opcode.kEntryOptional, ra, rb, rc));
  }
}
